package com.lwx.sys.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.lwx.sys.realm.UserRealm;
import com.lwx.sys.utils.EnvUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro的自动配置类
 *
 * @author gt
 */
@Data
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "shiro")
public class ShiroAutoConfiguration {
    /**
     * 加密方式
     */
    private String hashAlgorithmName = "md5";
    /**
     * 散列次数
     */
    private int hashIterations = 2;
    private String targetBeanName = "shiroFilter";
    private boolean targetFilterLifecycle = true;
    /**
     * 未登陆跳转
     */
    private String loginUrl = "/system/toLogin";
    /**
     * 登出跳转
     */
    private String loginOutUrl = "/system/logout";
    /**
     * 未授权跳转
     */
    private String unauthorizedUrl = "/unauthorized.html";
    private String[] anonUrls;
    private String[] authcUrls;

    /**
     * 创建ShiroFilterFactoryBean  负责拦截所有请求
     */
    @Bean(value = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 注入安全管理器
        factoryBean.setSecurityManager(securityManager);
        // 设置非登陆跳转的页面
        factoryBean.setLoginUrl(loginUrl);
        // 未授权的跳转页
        factoryBean.setUnauthorizedUrl(unauthorizedUrl);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        //设置放行的url
        if (anonUrls != null && anonUrls.length > 0) {
            for (String url : anonUrls) {
                filterChainDefinitionMap.put(url, "anon");
            }
        }
        filterChainDefinitionMap.put(loginOutUrl,"logout");
        //设置拦击的url
        if (authcUrls != null && authcUrls.length > 0) {
            for (String url : authcUrls) {
                filterChainDefinitionMap.put(url, "authc");
            }
        }
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return factoryBean;
    }

    /**
     * 声明安全管理器
     */
    @Bean
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入realm
        securityManager.setRealm(userRealm);
        // 注入session管理器
        securityManager.setSessionManager(sessionManager());
        // 注入缓存管理器
        securityManager.setCacheManager(redisCacheManager());
        return securityManager;
    }

    /**
     * 声明userRealm
     */
    @Bean
    public UserRealm userRealm(HashedCredentialsMatcher credentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        // 注入凭证匹配器
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }


    /**
     * 声明凭证匹配器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        // 注入加密方式
        credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
        // 注入散列次数
        credentialsMatcher.setHashIterations(hashIterations);
        return credentialsMatcher;
    }

    /**
     * 配置shiro的代理过滤器
     */
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBeanDelegatingFilterProxy() {
        // 创建注册器
        FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();
        // 创建过滤器
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        // 设置过滤器的参数
        proxy.setTargetBeanName(targetBeanName);
        proxy.setTargetFilterLifecycle(targetFilterLifecycle);
        // 注册
        registrationBean.setFilter(proxy);
        Collection<String> servletNames = new ArrayList<>();
        servletNames.add(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        registrationBean.setServletNames(servletNames);
        return registrationBean;
    }


    /**
     * 这里是为了能在html页面引用shiro标签，下面两个函数必须添加，不然会报错
     *
     * @return new ShiroDialect();
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 加入对注解的支持
     * 可以在controller中的方法前加上注解
     * 如 @RequiresPermissions("userInfo:add")
     * @param securityManager 安全管理器
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisCacheManager redisCacheManager() {
        log.info("===========创建缓存管理器RedisCacheManager");

        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        //redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
        redisCacheManager.setPrincipalIdFieldName("user");
        //用户权限信息缓存时间
        redisCacheManager.setExpire(200000);
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        log.info("=========创建RedisManager,连接Redis");
        RedisManager redisManager = new RedisManager();
        //redisManager.setHost(EnvUtil.getString("spring.redis.host")+":"+EnvUtil.getString("spring.redis.port"));
        redisManager.setHost("112.124.37.170:8087");
        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setExpire(2000);
        return redisSessionDAO;
    }
}
